package com.strato.deploy.site;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

import com.strato.deploy.common.BundleInfo;
import com.strato.deploy.common.PackageInfo;
import com.strato.deploy.common.SiteInfo;
import com.strato.deploy.common.XStreamCreator;
import com.thoughtworks.xstream.XStream;

public class Generator {

	public void generate(File dir,OutputStream xmlOut){
		List<File> filenames=new ArrayList<File>();
		collect(dir,filenames);
		SiteInfo site=new SiteInfo();
		List<BundleInfo> bundles=new ArrayList<BundleInfo>();
		site.setBundles(bundles);
		for(File jar:filenames){
			String relPath=extractRelativePath(jar,dir);
			Manifest manifest=extractManifest(jar);
			Attributes attrs = manifest.getMainAttributes();
			String bundleId=attrs.getValue("Bundle-SymbolicName");
			if(bundleId==null){
				System.out.println("Unable to find bundle-symbolic-name from "+relPath);
				continue;
			}
			String bundleVersion=attrs.getValue("Bundle-Version");
			BundleInfo bundleInfo=new BundleInfo();
			bundleInfo.setId(bundleId);
			bundleInfo.setVersion(bundleVersion);
			bundleInfo.setPath(relPath);
			bundles.add(bundleInfo);

			String importPackage=attrs.getValue("Import-Package");
			String exportPackage=attrs.getValue("Export-Package");
			String requiredBundle=attrs.getValue("Require-Bundle");
			
			fillImportPackages(bundleInfo,importPackage);
			fillExportPackages(bundleInfo,exportPackage);
			fillRequiredBundles(bundleInfo,requiredBundle);
		}
		XStream xstream=XStreamCreator.create();
		xstream.toXML(site, xmlOut);
	}

	private void fillRequiredBundles(BundleInfo bundleInfo,
			String requiredBundle) {
		if(requiredBundle==null){
			return;
		}
		requiredBundle=requiredBundle.trim();
		String[] bundleNames = requiredBundle.split(",");
		List<BundleInfo> requiredBundles=new ArrayList<BundleInfo>();
		for(int i=0;i<bundleNames.length;i++){
			String bundleName=bundleNames[i].trim();
			int index=bundleName.indexOf(";");
			BundleInfo rBundle=new BundleInfo();
			String version=null;
			if(index!=-1){
				String versionExpr=bundleName.substring(index);
				bundleName=bundleName.substring(0, index);
				version=versionExpr.substring("bundle-version=\"".length()+1, versionExpr.length()-1);
			}
			rBundle.setId(bundleName);
			rBundle.setVersion(version);
			requiredBundles.add(rBundle);
		}
		bundleInfo.setRequiredBundles(requiredBundles);
	}

	private void fillExportPackages(BundleInfo bundleInfo, String exportPackage) {
		if(exportPackage==null){
			return;
		}
		exportPackage=exportPackage.trim();
		String[] packages=exportPackage.split(",");
		List<PackageInfo> exportPackages=new ArrayList<PackageInfo>();
		for(int i=0;i<packages.length;i++){
			String packageName=packages[i].trim();
			PackageInfo packageInfo=new PackageInfo();
			packageInfo.setName(packageName);
			exportPackages.add(packageInfo);
		}
		bundleInfo.setExportPackages(exportPackages);
	}

	private void fillImportPackages(BundleInfo bundleInfo, String importPackage) {
		if(importPackage==null){
			return;
		}
		importPackage=importPackage.trim();
		String[] packageNames=importPackage.split(",");
		List<PackageInfo> packages=new ArrayList<PackageInfo>();
		for(int i=0;i<packageNames.length;i++){
			String packageName=packageNames[i].trim();
			int index=packageName.indexOf(";");
			PackageInfo packageInfo=new PackageInfo();
			String version=null;
			if(index!=-1){
				String versionExpr=packageName.substring(index);
				packageName=packageName.substring(0, index);
				version=versionExpr.substring("version=\"".length()+1, versionExpr.length()-1);
			}
			packageInfo.setName(packageName);
			packageInfo.setVersion(version);
			packages.add(packageInfo);
		}
		bundleInfo.setImportPackages(packages);
	}

	private Manifest extractManifest(File jar) {
		try {
			@SuppressWarnings("resource")
			JarInputStream jarIs=new JarInputStream(new FileInputStream(jar));
			return jarIs.getManifest();
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	private String extractRelativePath(File jar, File dir) {
		return jar.getAbsolutePath().substring(dir.getAbsolutePath().length()).replaceAll("\\\\", "/");
	}

	private void collect(File dir, List<File> filenames) {
		File[] dirs = dir.listFiles(new JarFileFilter(filenames));
		if(dirs!=null){
			for(int i=0;i<dirs.length;i++){
				collect(dirs[i],filenames);
			}
		}
	}
	
	private class JarFileFilter implements FileFilter{

		List<File> collection;
		
		public JarFileFilter(List<File> collection) {
			super();
			this.collection = collection;
		}

		@Override
		public boolean accept(File pathname) {
			if(pathname.getName().startsWith(".")){
				return false;
			}
			if(pathname.isDirectory()){
				return true;
			}
			if(pathname.getName().toLowerCase().endsWith(".jar")){
				collection.add(pathname);
			}
			return false;
		}
		
	}
}
